#!/bin/ash

boot_version=Ver.20101221

# Archlive 函数库
# 基于 Linux live scripts 6 修改
# Functions library :: for Linux Live scripts 6
# Author: Tomas M. <http://www.linux-live.org>

# ===========================================================
# GLOBAL variables
# ===========================================================
# linux live flag to fstab, if fstab line doesn't contain it,
# never remove it from fstab automatically (user added it)
FSTABLLFLAG="#Archlive-Update"

# We have to set these variables very carefully
#UNION=/union
UNION=/new_root
MEMORY=/memory
MOUNTDIR=/mnt
CHANGES=$MEMORY/changes
XINO=$MEMORY/xino
COPY2RAM=$MEMORY/copy2ram
IMAGES=$MEMORY/images
INITRAMDISK=$MOUNTDIR/live
DATADIR=$MOUNTDIR/data
LOOPMOUNT=$MOUNTDIR/tmp
XZ_PATH=$DATADIR/addons
MERGE_PATH=$DATADIR/modules

# live系统制作脚本会自动更改如下参数，切忌手动修改，否则可能无法启动
# this will be replaced by build script, so never change the following line!
export LIVECDNAME="archlive"
export overlayname=""
export BOOT_LOG="/var/log/live_boot.log"

RST="\033[1;0m"

# regular colors
K="\033[0;30m"    # black
R="\033[0;31m"    # red
G="\033[0;32m"    # green
Y="\033[0;33m"    # yellow
B="\033[0;34m"    # blue
M="\033[0;35m"    # magenta
C="\033[0;36m"    # cyan
W="\033[0;37m"    # white

# emphasized (bolded) colors
EMK="\033[1;30m"
EMR="\033[1;31m"
EMG="\033[1;32m"
EMY="\033[1;33m"
EMB="\033[1;34m"
EMM="\033[1;35m"
EMC="\033[1;36m"
EMW="\033[1;37m"

# background colors
BGK="\033[40m"
BGR="\033[41m"
BGG="\033[42m"
BGY="\033[43m"
BGB="\033[44m"
BGM="\033[45m"
BGC="\033[46m"
BGW="\033[47m"

#hl () { printf "${W}$@${RST}"; }
#hg () { printf "${EMG}$@${RST}"; }
#hy () { printf "${EMY}$@${RST}"; }
#hr () { printf "${EMR}$@${RST}"; }

hl () { echo -ne "${W}$@${RST}"; }
hg () { echo -e "${EMG}$@${RST}"; }
hy () { echo -e "${EMY}$@${RST}"; }
hr () { echo -e "${EMR}$@${RST}"; }

info () {
    if [ $# -eq 2 ] && [ "$1" = "-n" ]; then
        echo -ne " $EMB*$RST "; hl "$2"; 
    else
        echo -ne " $EMB*$RST "; hl "$@"; echo
    fi
}

msg () {
    if [ "${quiet}" != "y" ]; then
	if [ $# -eq 2 ] && [ "$1" = "-n" ]; then
		echo -ne " $EMB*$RST "; hl "$2"; 
	else
		echo -ne " $EMB*$RST "; hl "$@"; echo
	fi
    fi
}

error () {
    if [ $# -eq 2 ] && [ "$1" = "-n" ]; then
        echo -ne " $EMR*$RST "; hl "$2"
    else
        echo -ne " $EMR*$RST "; hl "$@"; echo
    fi
}
err () {
    if [ $# -eq 2 ] && [ "$1" = "-n" ]; then
        echo -ne " $EMR*$RST "; hl "$2"
    else
        echo -ne " $EMR*$RST "; hl "$@"; echo
    fi
}

warn () {
    if [ $# -eq 2 ] && [ "$1" = "-n" ]; then
        echo -ne " $EMY*$RST "; hl "$2"
    else
        echo -ne " $EMY*$RST "; hl "$@"; echo
    fi
}

# Checking boot paremeters 检查启动参数
check_boot_paremeters ()
{
    for cmd in ${CMDLINE}; do
	case "${cmd}" in
        \#*) break ;; # ignore everything after a # in the commandline
        # The kernel passes those to the kernel on its own
        [0123456Ss]) ;;
        [0-9]*) ;;
        single) ;;
        rw) readwrite="yes" ;;
        ro) readwrite="no" ;;
        # only export stuff that does work with ash :)
        *=*) rhs="$(echo "${cmd}" | cut -d= -f2-)"
             cmd="$(echo "${cmd}" | cut -d= -f1 | sed 's|\.|_|g')"
             cmd="$(echo "${cmd}" | sed 's|-|_|g')=${rhs}"
             (echo "${cmd}" | grep -qe '^[0-9]') || export "${cmd}"
             ;;
        *)   cmd="$(echo "${cmd}" | sed 's|\.|_|g')"
             cmd="$(echo "${cmd}" | sed 's|-|_|g')"
             (echo "${cmd}" | grep -qe '^[0-9]') || export "${cmd}=y"
             ;;
	esac
    done
}

poll_device() {
    device="$1"
    if [ "$2" -ge 0 ]; then
        seconds="$2"
    else
        seconds=5
    fi
    if [ "${udevd_running}" -eq 1 ]; then
        echo "Waiting ${seconds} seconds for device ${device} ..."
        while [ ! -b "${device}" -a ! -h "${device}" -a ${seconds} -gt 0 ]; do
            sleep 1
            seconds=$((${seconds}-1))
        done
    fi
    [ -b "${device}" -o -h "${device}" ]
}

default_mount_handler() {
    if [ ${root:0:5} != "/dev/" ] || ! poll_device "${root}" ${rootdelay}; then
        msg "Root device '${root}' doesn't exist. Attempting to create it."
        major=""
        minor=""
        if [ ${root:0:5} = "/dev/" ]; then
            # It might be a block device (/dev/sda) -> /sys/block/sda/dev
            # or a partition (/dev/sda1) -> /sys/block/sda/sda1/dev
            for dir in /sys/block /sys/block/*; do
                if [ -f ${dir}/${root:5}/dev ]; then
                    major="$(cat ${dir}/${root:5}/dev | cut -d: -f1)"
                    minor="$(cat ${dir}/${root:5}/dev | cut -d: -f2)"
                    break
                fi
            done
        # It might be a major/minor pair (8:1)
        elif echo ${root} | grep -q :; then
            major="$(echo ${root} | cut -d: -f1)"
            minor="$(echo ${root} | cut -d: -f2)"
            root="/dev/root"
        # It might be major/minor encoded as a single hex-number (lilo-style) (801)
        elif [ ${#root} -le 4 -a ${#root} -gt 2 ] && echo "${root}" | grep -qe '^[A-Fa-f0-9]*$'; then
            str_offset=$((${#root}-2))
            major=$((0x${root:0:${str_offset}}))
            minor=$((0x${root:${str_offset}}))
            root="/dev/root"
        fi
        if [ -n "${major}" -a -n "${minor}" ]; then
            msg "Creating root device ${root} with major ${major} and minor ${minor}."
            mknod ${root} b ${major} ${minor}
        else
            err "Unable to determine major/minor number of root device '${root}'."
            echo "You are being dropped to a recovery shell"
            echo "    Type 'exit' to try and continue booting"
            launch_interactive_shell
            msg "Trying to continue (this will most likely fail) ..."
        fi
    fi
    # We didn't build filesystem support into busybox,
    # instead we use util-linux-ng's blkid for best compatibility
    if [ -n "${rootfstype}" ]; then
        fstype="${rootfstype}"
    else
        fstype=$(sbin/blkid -u filesystem -o value -s TYPE -p "${root}")
        if [ -z "${fstype}" ]; then
            err "Unable to determine the file system type of ${root}:"
            echo "Either it contains no filesystem, an unknown filesystem,"
            echo "or more than one valid file system signature was found."
            echo
            echo "Try adding"
            echo "    rootfstype=your_filesystem_type"
            echo "to the kernel command line."
            echo
            echo "You are now being dropped into an emergency shell."
            launch_interactive_shell
            msg "Trying to continue (this will most likely fail) ..."
        fi
    fi
    if [ "${readwrite}" = "yes" ]; then
        rwopt="rw"
    else
        rwopt="ro"
    fi
    if [ -L "${root}" ]; then
      root=$(readlink -f "${root}")
    fi
    mount ${fstype:+-t ${fstype}} -o ${rwopt}${rootflags:+,${rootflags}} "${root}" "$1"
}

#=================================================================
# debug and output functions
#=================================================================

# global variable
if [ "$break" = "y" ] || [ "$debug" = "y" ]; then
	export DEBUG_IS_ENABLED="y"
fi

debug_log()
{
   if [ "$DEBUG_IS_ENABLED" ]; then
      echo "  - debug: $*" >&2
      log "- debug: $*"
   fi
}

# echogreen will echo $@ in green color
# $1 = text
#
echogreen()
{
   echo -ne " $EMB""$@""$RST"
}

# echolog
# $1 = text to show and to write to /var/log/messages
#
echolog()
{
   if [ "$1" != "" ]; then
      echogreen "* "
      log "LIVECD:" "$@" 
      echo "$@"
   fi
}

# log
# store given text in /var/log/livedbg
log()
{
   echo "$@" 2>/dev/null >>${BOOT_LOG}
}

# show information about the debug shell
show_debug_banner()
{
   echo
   echo "====="
   if [ "$cn" = "y" ]; then
	echo ": Debug 模式开启，此处开启root权限的命令行模式."
	echo ": Ctrl+D 退出该模式继续."
   else
	echo ": Debugging started. Here is the root shell for you."
	echo ": Type your desired commands or hit Ctrl+D to continue booting."
   fi
   echo
}

# 调试用测试点 (当启动参数包含debug 或者 break 时启用)
# debug_shell: executed when "debug" or "break" boot parameter is present
debug_shell()
{
   if [ "$DEBUG_IS_ENABLED" ]; then
	show_debug_banner
#	ash < /dev/console 2>/dev/null
#	echo
	PS1='[ramfs \W]\$ ' /bin/sh -i 2>/dev/null
   fi
}

launch_interactive_shell() {
    export PS1='[ramfs \W]\$ '
    [ "$1" = "--exec" ] && exec /bin/sh -i
    /bin/sh -i
}


# header
# $1 = text to show
#
header()
{
   echo -e "\033[0;1m""$@""\033[0;0m"
}

fatal()
{
   echolog
   if [ "$cn" = "y" ]; then
	header "出现致命错误 - $1"
	echolog "Ctrl+Alt+Delete 重新启动电脑..."
   else
	header "Fatal error occured - $1"
	echolog "Please reboot your computer with Ctrl+Alt+Delete ..."
   fi
   echolog
   ash < /dev/console 2>/dev/null
}

allow_only_root()
{
  # test if the script is started by root user. If not, exit
  if [ "$UID" -ne 0 ]; then
	if [ "$cn" = "y" ]; then
		echo "只有root用户可执行 $(basename $0)"; exit 1
	else
		echo "Only root can run $(basename $0)"; exit 1
	fi
  fi
}

# ===========================================================
# text processing functions
# ===========================================================

# look into cmdline and echo $1 back if $1 is set
# $1 = value name, case sensitive, for example 'debug'
# 检查是否存在 $1 内核启动参数，如果存在则返回$1参数
# 区分大小写
cmdline_parameter()
{
   debug_log "cmdline_parameter" "$*"
   log "            searching for bootparam: $1"
   egrep -o "(^|[[:space:]])$1([[:space:]]|\$)" /proc/cmdline | tr -d " " | tail -n 1
}

# look into cmdline and echo value of $1 option
# $1 = value name, case sensitive, for example 'changes'
# 检查是否存在 $1 内核启动变量，如果存在则返回变量的值
# 区分大小写  例如 内核启动变量root=/dev/sda8  返回/dev/sda8
cmdline_value()
{
   debug_log "cmdline_value" "$*"
   log "            searching for bootparam value: $1"
   egrep -o "(^|[[:space:]])$1=[^[:space:]]+" /proc/cmdline | cut -d "=" -f 2- | tail -n 1
}

# Make sure the part of a script after 'mutex_lock' call is atomic,
# that means the 'locked' part of the script can never be execuetd 
# from several processes at the same time, in parallel.
# Every script waits until it gathers the lock.
# The lock directory is saved in /dev instead of /tmp, because /tmp may be
# readonly at the time when the lock is needed (eg. when udev is starting)
# $1 = name of the lock
#
mutex_lock()
{
   debug_log "mutex_lock" "$*"
   while ! mkdir "/dev/ll-mutex-lock-$1" 2>/dev/null; do
      usleep 100000;
   done
}

# Unlock the lock so another waiting process can reusse it and continue
# $1 = name of the lock
#
mutex_unlock()
{
   debug_log "mutex_unlock" "$*"
   rmdir "/dev/ll-mutex-lock-$1" 2>/dev/null
}

# ===========================================================
# system functions
# ===========================================================

# setup /usr from /usr.lzm inside initrd
mount_initrd_loops()
{
   debug_log "mount_initrd_loops" "$*"
   if [ -e /usr.lzm ]; then
      mount_device /usr.lzm /usr loop,ro squashfs
   fi
   if [ -e /drivers.lzm ]; then
      mount_device /drivers.lzm /lib/modules/*/kernel/drivers loop,ro squashfs
   fi
}

# modprobe module $1, including all dependencies, suppress all messages
# This was own function, because modprobe in busybox didn't support
# neither gzipped modules nor dependencies. Seems to be fixed now, though.
# $1 = module name, eg. ehci-hcd
# $* = optional arguments
#
modprobe_module()
{
   debug_log "modprobe_module" "$*"
   local MODULE

   MODULE="$1"
   shift

   if [ ! "$MODULE" ]; then return 1; fi
   modprobe "$MODULE" $* 2>/dev/null
}

cdname()
{
	if [ "$(cmdline_value cdname)" != "" ]; then
		LIVECDNAME="$(cmdline_value cdname)"
	else
		LIVECDNAME="${LIVECDNAME}"
	fi
#	if [ "x$cdname" != "x" ]; then LIVECDNAME=$cdname; fi
}

# mknod next loop device
# - find biggest loop device in /dev/loop/, assume it to be used
# - preallocate (mknod) 20 more loop devices in one round
mknod_next_loop_dev()
{
   debug_log "mknod_next_loop_dev" "$*"
   local i NR END PFX

   mutex_lock mknod_next_loop_dev

   if [ -d /dev/loop ]; then
      NR=$(find /dev/loop/ -maxdepth 1 | sed -r 's/[^0-9]+//' | sort -n | tail -n 1)
      PFX="/"
   else
      NR=$(find /dev/ -maxdepth 1 | grep loop | sed -r 's/[^0-9]+//' | sort -n | tail -n 1)
      PFX=""
   fi
   NR=$(expr 0$NR + 1)
   END=$(expr 0$NR + 20)
   for i in $(seq $NR $END); do
      mknod /dev/loop$PFX$i b 7 $i 2>/dev/null
   done
   echo /dev/loop$PFX$NR

   mutex_unlock mknod_next_loop_dev
}

# ===========================================================
# Filesystem functions
# ===========================================================

# Find out what locale is requested
# If no locale is given, use the firts one available (if any)
# $1 = locale (optional argument, if exists, no autodetection is made)
locale_id()
{
   debug_log "locale_id" "$*"
   local LOCALE i

   # first try to find out locale from boot parameters
   LOCALE="$1"
   if [ "$LOCALE" = "" ]; then LOCALE=$locale; fi
   if [ "$LOCALE" = "" ]; then LOCALE=$language; fi
   if [ "$LOCALE" = "" ]; then LOCALE=$lang; fi

   # if not found, set it to locale from usr/lib/locale,
   # but only if there is just ONE directory, nothing more
   # (so we are sure which one to use)
   if [ "$LOCALE" = "" ]; then
      for LOCALE in $(ls -A1p /usr/lib/locale 2>/dev/null | grep / | sed -r "s:[/]|[.].*::"); do
         i="1$i"
      done
      if [ "$i" != "1" ]; then LOCALE=""; fi
   fi

   if [ "$LOCALE" != "" -a -e /usr/share ]; then
      cat /usr/share/locale/locale.alias | sed -r "s/#.*//" | egrep "$LOCALE|$LOCALE""_" | tail -n 1 | tr -s "[[:space:]]" " " | cut -d " " -f 2- | tr -d " "
   fi
   debug_log "LOCALE=$LOCALE"
}

# Find out what iocharset to use
iocharset()
{
   debug_log "iocharset" "$*"
   local CHARSET IOCHARSET

   # if iocharset is explicitly set at the boot prompt,
   # return it regardless the locale settings
   IOCHARSET=$(cmdline_value iocharset)
   if [ "x$IOCHARSET" = "x" -o "$cn" ]; then
	# 如果启动参数中包含 cn 或者启动参数无指定iocharset 则使用如下默认值
	IOCHARSET="zh_CN.utf8"
   else
	IOCHARSET=$(iocharset)
   fi

   if [ "$IOCHARSET" != "" ]; then
      echo $IOCHARSET
      return 0;
   fi

   # else find out the iocharset from locale_id output, it should match
   # some kernel module (after stripping out few of the dashes)
   IOCHARSET=$(locale_id | cut -d . -f 2- | tr "[[:upper:]]" "[[:lower:]]" | tr -d -)
   if [ "$IOCHARSET" = "" ]; then return 0; fi

   find /lib/modules -name "nls_*" | sed -r 's:^.*/|[.]ko$::g' | cut -b 5- | while read CHARSET; do
      if [ "$(echo $CHARSET | tr "[[:upper:]]" "[[:lower:]]" | tr -d -)" = "$IOCHARSET" ]; then
         echo "$CHARSET"
         return 0
      fi
   done
   return 1
   debug_log "IOCHARSET=$IOCHARSET"
}

# Get filesystem options
# $1 = filesystem
# $2 = 'fstab' or 'mount' ... 'auto'/'noauto' string is enabled (fstab) or disabled (mount)
#

fs_options()
{
   debug_log "fs_options" "$*"
   local NOAUTO IOCHARSET

   NOAUTO=$(cmdline_parameter noauto)
   debug_log "NOAUTO=$NOAUTO"
   if [ "$NOAUTO" = "" ]; then NOAUTO="auto"; fi
   #if [ "$1" = "fuse.ntfs" -o "$1" = "ntfs" ]; then NOAUTO="force,norelatime"; fi
   if [ "$1" = "fuse.ntfs" -o "$1" = "ntfs" ]; then NOAUTO="norelatime"; fi
   if [ "$2" = "fstab" ]; then echo -n "$NOAUTO" ; fi
   if [ "$1" = "swap" ]; then echo ",defaults,pri=1"; return 0; fi


   IOCHARSET=$iocharset
   if [ "x$IOCHARSET" = "x" -o "$cn" ]; then
	# 如果启动参数中包含 cn 或者启动参数无指定iocharset 则使用如下默认值
	IOCHARSET="zh_CN.utf8"
   else
	IOCHARSET=$(iocharset)
   fi

   if [ "$1" = "vfat" ]; then
      echo -n ",quiet,umask=0,check=s,shortname=mixed,utf8"
   fi

   if [ "$1" = "iso9660" ]; then
      echo -n ",ro,utf8"
   fi

   if [ "$1" = "fuse.ntfs" -o "$1" = "ntfs" ]; then
      echo -n ",gid=100,fmask=0113,dmask=0002,utf8"
#      if [ "$IOCHARSET" ]; then
#         echo ",locale=$IOCHARSET"
#      fi
   fi

   if [ "$1" = "ntfs-3g" ]; then
      echo ",users,uid=1000,gid=100,fmask=0113,dmask=0002,utf8"
   fi
}

# discover filesystem used on the given device
# Use vfat for msdos filesystem. Use ntfs-3g for ntfs if ntfsmount exists.
# $1 = device, eg. /dev/hda1
#
device_filesystem()
{
   debug_log "device_filesystem" "$*"
   local NTFS

   #if [ -e /usr/bin/ntfsmount ]; then NTFS="fuse.ntfs"; else NTFS="ntfs-3g"; fi
   if [ -e ntfs-3g ]; then NTFS="ntfs-3g"; else NTFS="ntfs"; fi
   blkid -s TYPE "$1" -o value | sed "s/msdos/vfat/" | sed "s/ntfs/$NTFS/"
}

# tell us if the given filesystem is supported
# (eg. it's in /proc/filesystems or we know it)
# $1 = filesystem name
#
is_supported_filesystem()
{
   debug_log "is_supported_filesystem" "$*"

   if [ -e /usr/bin/ntfsmount -o "$1" = "ntfs-3g" ]; then
      return 0
   fi

   # the following command will set the return value
   egrep -q "[[:space:]]$1\$" /proc/filesystems
}

# Mount device $1 to $2
# If the device is using vfat or ntfs filesystem, use iocharset as a mount option
# $1 = /dev device to mount, eg. /dev/hda1, or loop file, or directory
# $2 = mountpoint, eg. /mnt/hda1
# $3 = optional mount options, for example "ro", or "remount,rw"
# $4 = optional filesystem name, in order to skip autodetection
#
mount_device()
{
   debug_log "mount_device" "$*"
   local FS DEV TARGET LOOPDEV OPTIONS FILESYSTEM ERR

   # make sure we have enough arguments
   if [ "$2" = "" ]; then return 1; fi
   if [ "$1" = "" ]; then rmdir "$2" 2>/dev/null; return 1; fi

   mkdir -p "$2"
   if [ $? -eq 0 ]; then
	debug_log "挂载点 $2 创建成功！ Mount point $2 created sucess!"
   else
	debug_log "挂载点$2已经存在? Mount point $2 already exits?"
   fi

   DEV="$1"
   TARGET="$2"
   if [ "$4" != "" ]; then FS="$4"; else FS=$(device_filesystem "$1"); fi
   if [ "$FS" ]; then OPTIONS=$(fs_options $FS mount); FS="-t $FS"; fi
   if [ "$OPTIONS" ]; then OPTIONS="$OPTIONS"; else OPTIONS=""; fi
   if [ -f "$DEV" ]; then OPTIONS="$OPTIONS,loop"; fi
   if [ -d "$DEV" ]; then OPTIONS="$OPTIONS,rbind"; fi
   if [ "$3" ]; then OPTIONS="$OPTIONS,$3"; fi

   OPTIONS=$(echo "$OPTIONS" | sed -r "s/^,+//")
   if [ "x$OPTIONS" = "x" ]; then
	OPTIONS=""
   else
	OPTIONS=" -o $OPTIONS"
   fi

   debug_log "OPTIONS=\"$OPTIONS\" FS=\"$FS\" DEV=\"$DEV\" TARGET=\"$TARGET\""

   if [ "$FS" = "-t ntfs-3g" ]; then
      ntfsmount "$DEV" "$TARGET" $OPTIONS 2>&1 >/dev/null
      ERR=$?
      debug_log "1st try: ERR=$ERR ntfsmount "$DEV" "$TARGET" $OPTIONS ..."
   else
      mount -n $OPTIONS $FS "$DEV" "$TARGET" 2>&1 >/dev/null
      ERR=$?
      debug_log "1st try: ERR=$ERR mount -n $OPTIONS $FS "$DEV" "$TARGET" ..."
   fi

   # not enough loop devices? try to create one.
   if [ $ERR -eq 2 ]; then
       debug_log "没有足够loop设备，尝试创建并重新挂载"
       debug_log "not enough loop devices? try to create one."

       LOOPDEV=$(mknod_next_loop_dev)
       OPTIONS=$(echo "$OPTIONS" | sed -r "s/,loop//g")
       losetup "$LOOPDEV" "$DEV" 2>&1 >/dev/null # busybox's losetup doesn't support -r
       if [ $? -ne 0 ]; then
          debug_log "losetup -r $LOOPDEV $DEV"
          losetup -r "$LOOPDEV" "$DEV" 2>&1 >/dev/null # force read-only in case of error
       fi
       debug_log "OPTIONS=\"$OPTIONS\" FS=\"$FS\" LOOPDEV=\"$LOOPDEV\" TARGET=\"$TARGET\""
       mount -n $OPTIONS $FS "$LOOPDEV" "$TARGET" 2>&1 >/dev/null
       ERR=$?
       debug_log "2nd try: ERR=$SRR mount -n $OPTIONS $FS "$LOOPDEV" "$TARGET" ..."
   fi


   # if nothing works, try to force read-only mount
   if [ $ERR -ne 0 ]; then
       debug_log "以上办法都无法挂载，尝试强行只读挂载."
       debug_log "None of above actions works! try to force read-only mount"
       debug_log "mount -n -r $OPTIONS $FS $DEV $TARGET"
       mount -n -r $OPTIONS $FS "$DEV" "$TARGET" 2>&1 >/dev/null
       ERR=$?
       debug_log "3rd try: ERR=$SRR ....................."
   fi

   if [ $ERR -ne 0 ]; then
        debug_log "$DEV 无法挂载到 $TARGET, 删除挂载点 $TARGET"
	debug_log "$DEV can't be mount to $TARGET, delete $TARGET"
	rmdir $TARGET 2>/dev/null
   fi

   if [ $ERR -eq 0 ]; then
	debug_log "$DEV 成功挂载到 $TARGET"
	debug_log "$DEV mounted to $TARGET"
   fi
   return $ERR
}

# 如果 $1 是一个挂载点，则ismountpoint返回 0, 否则返回1
# $1 = 目录或者 loop 文件
#
ismountpoint()
{
   debug_log "ismountpoint" "$*"
   local MDIR

   MDIR=$(readlink -f "$1")
   cat /proc/mounts | cut -d " " -f 2 | egrep "^$MDIR\$" >/dev/null 2>&1
}

# unmount all parameters. If the parameter is not mountpoint but
# it's a file or directory, umount the device where the file/dir is stored.
#
# First try normal umount, if that fails then remount read-only
# If -l parameter is specified, do lazy-umount when normal umount fails
# $1..$n = files/directories/devices to be unmounted
#
fumount()
{
   debug_log "fumount" "$*"
   local TARGET LAZY

   while [ "$1" ]; do
      if [ "$1" = "-l" ]; then LAZY="yes"; shift; fi
      TARGET=$(readlink -f "$1")
      if ! ismountpoint "$TARGET"; then
         if [ -f "$TARGET" -o -d "$TARGET" ]; then
            TARGET=$(df "$TARGET" 2>/dev/null | tail -n 1 | tr -s " " | cut -d " " -f 6)
            if [ "$TARGET" = "Mounted" ]; then TARGET=""; fi
         fi
      fi

      if [ "$TARGET" != "" ]; then
         umount -n "$TARGET" >/dev/null 2>&1
         if [ $? -ne 0 ]; then
            mount -n -o remount,ro -t ignored ignored "$TARGET" >/dev/null 2>&1
            if [ "$LAZY" ]; then umount -n -l "$TARGET" >/dev/null 2>&1; fi
         fi
      fi
      shift
   done
}

# ===========================================================
# live module functions
# ===========================================================

# Create module
# call mksquashfs with apropriate arguments
# $1 = directory which will be compressed to squashfs module
# $2 = output filesystem module file
# $3..$9 = optional arguments like -keep-as-directory or -b 123456789
#
create_module()
{
   debug_log "create_module" "$*"
   rm -f "$2" # overwrite, never append to existing file
   $MKSQUASHFS "$1" "$2" -b 256K -lzmadic 256K $3 $4 $5 $6 $7 $8 $9>/dev/null
   if [ $? -ne 0 ]; then return 1; fi
   chmod a-wx "$2" # remove execute and write attrib
   chmod a+r "$2" # add read for everyone
}

# 挂载squashfs格式模块到指定目录
# $1 = squashfs格式模块的位置
# $2 = 挂载的目标目录
#
mount_module()
{
   debug_log "mount_module" "$*"
   mount_device "$1" "$2" loop,ro squashfs
}

# Insert a directory tree $2 to an union specified by $1
# Top-level read-write branch is specified by it's index 0
# Using =rr enables aufs to optimize real readonly branches
# $1 = union absolute path (starting with /)
# $2 = path to data directory
#
union_insert_dir()
{
   debug_log "union_insert_dir" "$*"
   debug_log "mount -n -o remount,add:1:$2=rr aufs $1"
   mount -n -o remount,add:1:$2=rr aufs $1
}

# Find LZM or sqfs modules in given dir
# $1 = root directory of mounted DATA dir
# $2 ARCH  i686 or x86_64 or any
find_modules()
{
   debug_log "ARCH=$ARCH find_modules" "$*"
   if [ "$2" = "i686"  -o "$ARCH" = "i686" ]; then
	 append_cmd="grep -v x86_64"
   elif [ "$2" = "x86_64" -o "$ARCH" = "x86_64" ]; then
	append_cmd="grep -v i686"
   else
	append_cmd="grep -v *"
   fi
   if [ -d $1/base ]; then
	if [ "$baseonly" = "y" ]; then
   		find "$1/base" "$1/addons" "$1/optional" -name "*.sqfs" | ${append_cmd} 2>/dev/null | sort
   		find "$1/base" "$1/addons" "$1/optional" -name "*.lzm" | ${append_cmd} 2>/dev/null | sort
	else
   		find "$1/base" "$1/addons" "$1/modules" "$1/optional" -name "*.sqfs" | ${append_cmd} 2>/dev/null | sort
   		find "$1/base" "$1/addons" "$1/modules" "$1/optional" -name "*.lzm" | ${append_cmd} 2>/dev/null | sort
	fi
   fi
}

# List all modules in all directories (base, modules, optional)
# and filter out unneeded optional modules (not specified by load= kernel parameter)
# separator for load and noload arguments is "," or ";"
# $1 = root directory of mounted DATAdir
#
list_modules()
{
   debug_log "ARCH=$ARCH list_modules" "$*"
   local LOAD NOLOAD

   LOAD=$(cmdline_value load | sed -r 's/\*/.\*/g' | sed -r 's/,|;/|/g')
   #LOAD=$(echo $load | sed -r 's/\*/.\*/g' | sed -r 's/,|;/|/g')

   NOLOAD=$(cmdline_value noload | sed -r 's/\*/.\*/g' | sed -r 's/,|;/|/g')
   #NOLOAD=$(echo $noload | sed -r 's/\*/.\*/g' | sed -r 's/,|;/|/g')
   find_modules "$1" $ARCH | while read LINE; do
      MODNAME=$(echo $LINE | cut -b ${#1}- | cut -b 2-)
      if [ "$(echo $LINE | grep /optional/)" ]; then
         if [ ! "$LOAD" -o ! "$(echo $MODNAME | egrep -i "$LOAD")" ]; then continue; fi
      fi
      if [ "$NOLOAD" -a "$(echo $MODNAME | egrep -i "$NOLOAD")" ]; then continue; fi
      echo $LINE
   done
}

# Insert one single filesystem module to the union
# $1 = union absolute path
# $2 = module full path
# $3 = destination folder, where images will be mounted to
# $4 = preffix length strip (number of characters)
# 将$2模块挂载到$3并加入$1，去掉前缀$4
union_insert_module()
{
   debug_log "ARCH=$ARCH union_insert_module" "$*"
   local TARGET

   TARGET="$3/$(basename $2)"
   if ismountpoint $TARGET; then return 1; fi # skip already used modules
   mkdir -p $TARGET
   mount_module $2 $TARGET
   #if [ $? -ne 0 ]; then
#	if [ "$cn" = "y" ]; then
#		echo "无法读取模块. 下载的文件不完整?" >&2;
#	else
#		echo "Cannot read module data. corrupted download?" >&2
#	fi
#	return 1
 #  fi
   union_insert_dir $1 $TARGET
  # if [ $? -ne 0 ]; then
#	if [ "$cn" = "y" ]; then
#		echo "无法挂载模块到union" >&2
#	else
#		echo "can't insert module to union" >&2
#	fi
#	return 2
#   fi
   echo "$2" | cut -b $(($4+1))-
   echolog "$2" >/dev/null
   return 0
}

# Insert all filesystem modules from $2 directory and subdirectories, to the union
# $1 = union absolute path (starting with /)
# $2 = LiveCD data dir (with directories /base, /modules, etc.)
# $3 = destination folder, where images will be mounted to
# 将$2下的模块挂载到$3，并加入到$1
union_insert_modules()
{
   debug_log "ARCH=$ARCH union_insert_modules" "$*"
   local INSERTED

   list_modules $2 | while read MODULE; do
      INSERTED=$(union_insert_module $1 $MODULE $3 ${#2})
      if [ "$INSERTED" != "" ]; then echolog " -> $(echo $INSERTED | sed -r s:^/::)"; fi
   done
}

# Copy LiveCD modules to RAM directory
# will copy only /boot, and module files from $1
# $1 = data directory
# $2 = target directory in RAM
#
copy_to_ram()
{
   debug_log "copy_to_ram" "$*"
   cp -a "$1/rootcopy" "$2" 2>/dev/null # could be empty
   list_modules "$1" | while read MODULE; do
      TARGET=$(dirname "$MODULE" | cut -b ${#1}- | cut -b 2-)
      mkdir -p "$2/$TARGET"
      cp "$MODULE" "$2/$TARGET"
      if [ $? -ne 0 ]; then
		if [ "$cn" = "y" ]; then
			fatal "无足够内存，使用 ramsize=$RAMSIZE"
		else
			fatal "Not enough memory. Using ramsize=$RAMSIZE"
		fi
      fi
   done
}

# ===========================================================
# discovery functions
# ===========================================================

# List all supported network drivers
#
list_network_drivers()
{
   debug_log "list_network_drivers" "$*"

   # these drivers are probed in Slackware's initrd
   # (see initrd.img/scripts/network.sh).
   # I don't have personal experiences with most of these drivers
   # so I'll be happy if you report any particular one to be not working
   # (eg. causing hangups) in order to remove it from this list.

   echo 3c59x acenic atl1 de4x5 dgrs eepro100 e1000 epic100 hp100 ne2k-pci \
   olympic pcnet32 r8169 rcpci 8139too 8139cp sktr skge sky2 tulip via-rhine \
   yellowfin tg3 dl2k ns83820 depca ibmtr 3c501 3c503 3c505 3c507 3c509 3c515 \
   ac3200 acenic at1700 cosa cs89x0 de4x5 de600 de620 e2100 eepro eexpress \
   es3210 eth16i ewrk3 fmv18x forcedeth hostess_sv11 hp-plus hp lne390 ne3210 \
   ni5010 ni52 ni65 sb1000 sealevel smc-ultra sis900 smc-ultra32 smc9194 wd \
   ipw2100 ipw22pp iwl3945 prism54 b43 zd1211rw zd1201 rtl8180 ath5k ath9k \
   | tr " " "\n"
}

# List all CD-ROMs
# by using /proc entries
#
list_cdrom_devices()
{
   debug_log "list_cdrom_devices" "$*"
   local CDDEVICE

   for CDDEVICE in $(cat /proc/sys/dev/cdrom/info 2>/dev/null | head -n 3 | tail -n 1 | cut -d ":" -f 2); do
      echo "/dev/$CDDEVICE"
   done
}

# 列出所有已经挂载的目录(squashfs模块...)
# MOUNTDIR 默认为 /mnt
list_mounted_directories()
{
   debug_log "list_mounted_directories" "$*"
   if [ "$MOUNTDIR" ] && [ -d "$MOUNTDIR" ]; then
      ls -1 $MOUNTDIR | while read DIR; do
         if ismountpoint $MOUNTDIR/$DIR; then echo $DIR; fi
      done
   fi
}

# 列出电脑中所有的分区
# 如果启动参数中有nohd， 则返回空
list_partition_devices()
{
   debug_log "list_partition_devices" "$*"
   #if [ "$(cmdline_parameter nohd)" != "" ]; then return 1; fi
   if [ "$nohd" = "y" ]; then return 1; fi
   cat /proc/partitions | grep -v loop | grep -v major | grep -v '^$' | sed -r "s:^[0-9 ]+:/dev/:"
   if [ -e /dev/mapper/control ]; then # list LVM partitions if available
      ls -1 /dev/mapper/ | grep -v "^control\$" | sed -r "s:^:/dev/mapper/:"
   fi
}

# 列出所有硬盘类设备 /dev/sda 或者 /dev/sda  /dev/sdab...
list_disk_devices()
{
   debug_log "list_disk_devices" "$*"
   list_partition_devices | egrep -v "[0-9]"
}

# 列出所有标记为Linux swap的分区或者设备
# 启动参数中有nohd或者noswap时候，返回空
list_swap_devices()
{
   debug_log "list_swap_devices" "$*"

   #if [ "$(cmdline_parameter nohd)" != "" -o "$(cmdline_parameter noswap)" != "" ]; then return 1; fi
   if [ "$nohd" = "y" -o "$noswap" = "y" ]; then return 1; fi
   blkid -t TYPE="swap" -o device
}

# 列出所有块设备，启动参数有nocd的时候光盘类设备不列出
list_block_devices()
{
   debug_log "list_block_devices" "$*"
   #if [ "$(cmdline_parameter nocd)" = "" ]; then
   if [ "x$nocd" = "x" ]; then
      list_cdrom_devices
   fi
   list_partition_devices
}

# Format mountdir for device. This function used to append _cdrom or _removable
# suffix to the directory name so KDE was able to assign a nice icon for evey
# device, but this should be done using HAL in KDE nowadays, so we do not
# support these stupid suffixes anymore. Many people will be happy :)
# $1 = device full path, eg. /dev/hda1
#
device_mountdir()
{
   debug_log "device_mountdir" "$*"
   ROOT=$(cmdline_value root)
   #ROOT=$root
   if [ "$1" =  "$ROOT" ]; then
	echo "/"
   else
      echo "/$MOUNTDIR/$(basename "$1")" | tr -s /
   fi
}

# 在指定的设备查找文件/路径
# First it mounts the device read-only. If then the 'path' is found, 
# then remount without RO flag (causes it to be mounted read-write if possible)
# and return the path, else unmount and exit.
# If the device/dev_directory is already mounted, preserve it mounted
# $1 = device
# $2 = path/filename
#  在$1 上查找 $2， 先挂载并查找（如果已经挂载则保留原挂载路径）
#  如果找到则返回路径
#  没有找到则卸载
find_filepath()
{
   debug_log "find_filepath" "$*"
   local DIR FOUND PRESERVE

   DIR=$(device_mountdir $1)
   ismountpoint $DIR
   if [ $? -eq 0 ]; then
	debug_log "$1 已经挂载到 $DIR, 保留挂载"
      PRESERVE="true"
   else
      mount_device $1 $DIR rw
      if [ $? -ne 0 ]; then rmdir $DIR 2>/dev/null; return 1; fi
      PRESERVE=""
   fi

   FOUND=$(ls -A1d $DIR/$2 2>/dev/null | head -n 1 | tr -s '/')

   if [ "$FOUND" = "" ]; then
      if [ "$PRESERVE" != "true" ]; then
         fumount $DIR
         rmdir $DIR 2>/dev/null
      fi
      return 1
   else
      # remount without the 'ro' option now, so use rw or defaults
      # Only in the case it was not mounted already before.
      if [ "$PRESERVE" != "true" ]; then
         fumount $DIR
         mount_device $1 $DIR
         if [ $? -ne 0 ]; then
            rmdir $DIR 2>/dev/null
	    return 2
	 fi
      fi
      echo "$FOUND"
      return 0
   fi
}

# $1 = device
# $2 = path/filename

find_filepath1()
{
   debug_log "find_filepath" "$*"
   local DIR FOUND PRESERVE ERR

   # DIR 为设备 $1 的挂载点
   DIR=$(device_mountdir $1)

   # 判断 $DIR 是否是已经存在的挂载点
   ismountpoint $DIR
   if [ $? -eq 0 ]; then
      debug_log "$1 已经挂载到 $DIR, 保留挂载"
      PRESERVE="true"
   else
      mount_device $1 $DIR ro
      ERR=$?
      if [ $ERR -ne 0 ]; then
	debug_log "挂载不成功返回"
	rmdir $DIR 2>/dev/null
	return 1
      fi
      PRESERVE=""
   fi

   FOUND=$(find $DIR -type f -name "$(basename $2)" | head -n 1 | tr -s '/')
   if [ "$(basename $2)" != "$2" ]; then
	debug_log "$2 包含目录名"
	case $FOUND in $DIR/$2) debug_log "在$DIR下找到了$2, 找到了符合条件的启动标的...";
				   debug_log "FOUND=$FOUND";
				;;
			*) FOUND="" 	;;
		esac
   fi

   if [ "$FOUND" = "" ]; then
      if [ "$PRESERVE" != "true" ]; then
         fumount $DIR
         rmdir $DIR 2>/dev/null
      fi
      return 1
   else
      if [ "$PRESERVE" != "true" ]; then
         fumount $DIR
         mount_device $1 $DIR
         # 重新可写挂载或者按照默认选项挂载
         if [ $? -ne 0 ]; then
            rmdir $DIR 2>/dev/null
	    return 2
	 fi
      fi
      echo "$FOUND"
      debug_log "成功在$DIR下找到了$2"
      return 0
   fi
}

# 在电脑中查找文件/文件夹
# Find file in computer by mounting disks or other storage devices
# and searching for $1 in the mounted directory
# $1 = filename or device-path or devicepath/filename
#
find_file()
{
   debug_log "find_file" "$*"
   local FIND DEVICE DEVPART PATHPART

   # 允许使用/mnt/... 或者/dev/... 来指定设备名
   # 如果指定的为 /mnt/sda1/archlive 则转化为 /dev/sda1/archlive
   FIND=$(echo "$1" | sed -r "s:^/mnt/:/dev/:")

   ## if parameter is just a device, echo it and exit
   #if [ -b "$FIND" -o -c "$FIND" -o "$FIND" = "" ]; then echo "$FIND"; return; fi

   # If path doesn't start with /dev/, try to find the exact path on all devices
   # First, split DEV/PATH parts
   DEVPART=$(echo "$FIND" | egrep -o "^/dev/[^/]+")

   if [ "$DEVPART" = "" ]; then
      # 无查找no device is specified. Search all devices for filename $FIND
      PATHPART="$FIND";
	mounted_directories=$(list_mounted_directories)
	block_devices=$(list_block_devices)
      for DEVICE in ${mounted_directories} ${block_devices}; do
         if ! grep -q ":$DEVICE@$PATHPART:" /tmp/_findfile 2>/dev/null; then
            find_filepath "$DEVICE" "$PATHPART"
            if [ $? -eq 0 ]; then return 0; fi
            echo ":$DEVICE@$PATHPART:" >>/tmp/_findfile
         fi
      done
   else
      # try to find PATHPART only on the given device
      PATHPART=$(echo "$FIND" | sed -r 's:^/dev/[^/]+(.*):\1:')
      find_filepath $DEVPART $PATHPART
   fi
}

# 在电脑中查找
# 使用上面的find_file函数在电脑中查找指定的文件/目录
# if nothing found, sleep for a while to allow devices to settle and try again.
# (is there any way to find out if there are devices queued through /sys?)
# $1 = file or directory to find
#
find_in_computer()
{
   debug_log "find_in_computer" "$*"
   local TIMEOUT RESULT

   TIMEOUT=$(cmdline_value scantimeout | sed -r 's/[^0-9]*([0-9]+).*/\1/')
   #TIMEOUT=$(echo $scantimeout | sed -r 's/[^0-9]*([0-9]+).*/\1/')
   if [ "$TIMEOUT" = "" ]; then TIMEOUT=10; fi

   RESULT=$(find_file "$1")

   while [ $TIMEOUT -gt 0 -a "$RESULT" = "" ]; do
	if [ "$cn" = "y" ]; then
		echo -ne "- 请稍候...\r" >&2
	else
		echo -ne "- wait a while...\r" >&2
	fi
      sleep 1
      TIMEOUT=$((TIMEOUT-1))
      RESULT=$(find_file "$1")
   done
   debug_log "RESULT=$RESULT"
   echo $RESULT
}

# 根据基本模块上级目录名查找
# $1 基本模块上级目录名
# 此中方式避免$SGN错误指定导致无法找到基本模块的情况
find_by_folder ()
{
   if [ "$cn" = "y" ]; then
	debug_log "根据基本模块的上级目录名$1来查找"
   else
	debug_log "Search the modules by the directory name $1 of them..."
   fi
   debug_log "find_by_foler $1"
   DATAFROM=$(find_in_computer $1)
   debug_log "DATAFROM=$DATAFROM"
   if [ "$DATAFROM" ]; then
	if [ "$(find_modules $DATAFROM)" != "" ]; then
		if [ "$cn" = "y" ]; then
			echolog "在$DATAFROM找到模块文件"
		else
			echolog "Found modules in $DATAFROM."
		fi
		DATA=$DATAFROM
		debug_log "DATA=$DATA"
	fi
   fi
}

# Find and run all scripts from the given module
# This function is used by the activate and deactivate script when the distro
# is already started, not during live setup
# $1 = mounted module full path
# $2..$n = optional arguments for the scripts, eg. 'start'
#
find_n_run_scripts()
{
   debug_log "find_n_run_scripts" "$*"
   local MOD

   MOD="$1"
   shift

   if [ -d $MOD/etc/rc.d -o -d $MOD/etc/rc.d/init.d -o -d $MOD/etc/init.d ]; then
      ( find $MOD/etc/rc.d -type f -maxdepth 1 2>/dev/null ; \
        find $MOD/etc/init.d -type f -maxdepth 1 2>/dev/null  ; \
        find $MOD/etc/rc.d/init.d -type f -maxdepth 1 2>/dev/null \
      ) | cut -b ${#MOD}- | cut -b 2- | xargs -n 1 -r readlink -f | sort -u | while read SCRIPT; do
         if [ "$SCRIPT" != "" -a -x "$SCRIPT" -a ! -d "$SCRIPT" ]; then
            # call the script by real path, not from the module
            log "starting '$SCRIPT $@'"
            ${SCRIPT} "$@"
         fi
      done
   fi
}

# ===========================================================
# hardware preparation functions
# ===========================================================

# Create block devices to /dev described by /sys entries
#
mdev_start_hotplug()
{
   debug_log "mdev_start_hotplug" "$*"
   if [ "$cn" = "y" ]; then
	echolog "在/dev目录下创建块设备文件"
   else
	echolog "Creating /dev entries for block devices"
   fi
   mdev -s
   #rm /dev/pty??* /dev/tty??* # remove unneeded pty and tty devices
   echo mdev > /proc/sys/kernel/hotplug # use mdev as a hotplug handler
}

udev_start_hotplug()
{
   debug_log "mdev_start_hotplug" "$*"
   if [ "$cn" = "y" ]; then
	echolog "在/dev目录下创建块设备文件"
   else
	echolog "Creating /dev entries for block devices"
   fi
   echo > /proc/sys/kernel/hotplug
   udevd --daemon #Starting UDev Daemon
   udevadm trigger #"Triggering UDev uevents"
   echo udevd > /proc/sys/kernel/hotplug # use udevd as a hotplug handler
}

# Modprobe kernel modules needed for the LiveCD
#
modprobe_essential_modules()
{
   debug_log "modprobe_essential_modules" "$*"

   if [ "$cn" = "y" ]; then
	echolog "装载文件系统模块 ..."
   else
	echolog "Loading filesystems modules ..."
   fi
   modprobe_module loop
   modprobe_module isofs
   modprobe_module udf
   modprobe_module squashfs
   #modprobe_module aufs brs=1
   modprobe_module aufs
   modprobe_module ext2
   modprobe_module ext3
   modprobe_module ext4
   modprobe_module btrfs
   modprobe_module reiserfs
   modprobe_module xfs
   modprobe_module vfat
   modprobe_module fuse # for ntfs-3g
   modprobe_module ntfs # for ro driver
   modprobe_module btrfs
}

# Modprobe kernel modules needed for USB masstorage devices
#
modprobe_usb_modules()
{
   debug_log "modprobe_usb_modules" "$*"
   local LSPCI

   # skip module loading if nohotplug bootparam is present
   if [ "$nohotplug" = "y" -o "$(cmdline_parameter nohotplug)" ]; then return 0; fi

   LSPCI=$(lspci -v | grep -i prog-if)
   if [ "$(echo $LSPCI | egrep -i [eou]hci)" = "" ]; then
      return 0
   fi

   if [ "$cn" = "y" ]; then
	echolog "装载USB模块 ..."
   else
	echolog "Loading USB modules ..."
   fi
   if [ "$(echo $LSPCI | grep -i ehci)" != "" ]; then
      modprobe_module ehci-hcd
   fi
   if [ "$(echo $LSPCI | grep -i ohci)" != "" ]; then
      modprobe_module ohci-hcd
   fi
   if [ "$(echo $LSPCI | grep -i uhci)" != "" ]; then
      modprobe_module uhci-hcd
   fi
   modprobe_module usb-storage
}

# Load drivers for PCMCIA CardBus devices
#
modprobe_pcmcia_modules()
{
   debug_log "modprobe_pcmcia_modules" "$*"

   # skip module loading if nohotplug bootparam is present
   if [ "x$nohotplug" != "x" -o "$(cmdline_parameter nohotplug)" ]; then return 0; fi

   if [ "$cn" = "y" ]; then
	echolog "装载PCMCIA CardBus模块 ..."
   else
	echolog "Loading PCMCIA CardBus modules ..."
   fi
   modprobe_module pcmcia_core
   modprobe_module pcmcia
   modprobe_module rsrc_nonstatic
   modprobe_module yenta_socket
}

# 装载网卡驱动模块，直到找到 eth[0-9]
# Load network drivers unless eth[0-9] is found
modprobe_network_modules()
{
   debug_log "modprobe_network_modules" "$*"
   local ETH

   # 启动参数包含 "nohotplug" 则不载入网卡驱动模块
   # skip module loading if nohotplug bootparam is present
   if [ "x$nohotplug" != "x" ]; then return 0; fi

   # 检测硬件并载入相应驱动
   # probe all drivers. Start by the ones mentioned in pcimodules' output
   for module in $(list_network_drivers | egrep "$(pcimodules | tr "\n" "|")!") $(list_network_drivers); do
      modprobe_module $module
      ETH=$(cat /proc/net/dev | grep : | grep -v lo: | cut -d : -f 1 | tr -d " ")
      if [ "x$ETH" != "x" ]; then
         echo $ETH
         return 0
      fi
      rmmod $module 2>/dev/null
   done
}

# 初始化网络
# $1 = netdevice (eth0, ath0, wlan0)
# $2 = essid
# $3 = key
init_net()
{
   debug_log "start_init_net" "$*"
   if [ "x$2" != "x" ]; then
	ifconfig $1 up
	#  无线网络支持
	iwconfig $1 essid $2
	if [ "x$3" != "x" ]; then iwconfig $1 key $3; fi
	ipconfig $1
   else
	for i in $(ls /sys/class/net); do
		[ "$i" = "lo" ] && continue
		ipconfig $i
	done
   fi
   export NFS="$(ipconfig $1 | grep rootpath | awk -Frootpath: '{print $2}')"
}
# Start udhcpc to get IP address from DHCP server
# $1 = interface to use (optional)
#
init_dhcp()
{
   debug_log "start_dhcp_client" "$*"

   if [ "x$1" != "x" ]; then
	# 多网卡支持
	for i in $1; do
		ifconfig $1 up
		#  无线网络支持
		if [ "x$essid" != "x" ]; then iwconfig $i essid $essid; fi
		if [ "x$pass" != "x" ]; then iwconfig $i key $pass; fi
		udhcpc -i $1 -q
	done
   else
      ifconfig eth0 up
      udhcpc -q
   fi
}

# Mount http filesystem from the given server
# $1 = server
# $2 = targetmountdir
#
mount_httpfs()
{
   debug_log "mount_httpfs" "$*"

   mkdir -p $2
   httpfs $1 $2
}

# Mount cifs filesystem
# $1 = server $2 = targetmountdir
# $3 = user	$4= password
# user pass 直接在启动参数中指定
mount_cifs()
{
   debug_log "mount_cifs" "$*"

   mkdir -p $2
   user=$3
   pass=$4
   option="-o users,uid=1000,gid=100,file_mode=0777,dir_mode=0777,iocharset=utf8,rw"
   if [ "x$user" != "x" ]; then
	option="$option,username=$user,password=$pass"
   fi
   mount.cifs $1 $2 $option
}

# Unload modules loaded to kernel which are not used
# This function used to unload more modules, but it may cause
# problems to auto-remove some of them (eg. a network module 
# can seem unneeded even if network is to be used very soon.
#
rmmod_unused_modules()
{
   debug_log "rmmod_unused_modules" "$*"
   rmmod usb-storage uhci-hcd ohci-hcd ehci-hcd 2>/dev/null
   rmmod yenta_socket rsrc_nonstatic pcmcia pcmcia_core 2>/dev/null
}

# kill all unneeded processes, which have bigger PID then the PID of
# current shell. We can't use killall5, as it would kill some processes
# which may be currently needed, for example ntfsmount.
# $1 = maximum pid (kill all lower)
#
killall_unneeded()
{
   debug_log "killall_unneeded" "$*"
   local LIST PID

   PID=$1
   for pid in $(ps | grep -v "PID" | egrep -v "\[.*\]" | fgrep -v mount | fgrep -v posixovl | fgrep -v ntfs | sed -r "s/^[[:space:]]*([0-9]+).*/\\1/"); do
      if [ $pid -lt $PID ]; then
         LIST="$LIST $pid"
      fi
   done

   kill -SIGTERM $LIST 2>/dev/null # SIGTERM
   sleep 2
   kill -SIGKILL $LIST 2>/dev/null # SIGKILL
}

# enable/disable CD autoejecting when unmounted
# $1 = 1|0 ... enable|disable
#
cd_autoeject()
{
   debug_log "cd_autoeject" "$*"
   echo $1 >/proc/sys/dev/cdrom/autoeject
}

# ===========================================================
# FSTAB functions
# ===========================================================

# $1 = fstab file
# $2 = device name
dev_is_in_fstab()
{
   debug_log "dev_is_in_fstab" "$*"
   cat "$1" | sed -r "s/#.*//" | egrep -q "^[[:space:]]*$2[[:space:]]"
}

# update given line in fstab, add new values only if the device is not found
# $1 = fstab file to parse
# $2 = device name
# $3 = mountpoint
# $4 = filesystem
# $5 = mount options
#
fstab_add_line()
{
   debug_log "fstab_add_line" "$*"
   local DIR

   if [ "$4" != "swap" ]; then DIR="$3"; else DIR="none"; fi
   if [ "$2" = "none" -o "$2" = "tmpfs" ]; then
	echo "$2" "$DIR" "$4" "$5" 0 0 "$FSTABLEFLAG" >>$1
   fi
   if ! dev_is_in_fstab "$1" "$2"; then
      echo "$2" "$DIR" "$4" "$5" 0 0 "$FSTABLEFLAG" >>$1
   fi
}

# create correct fstab file in $1/etc/fstab and create apropriate
# mount directories in $1/mnt. This function is only calld once,
# during liveCD startup (even before init from the distro is started).
# $1 = root directory (union)
#
fstab_update()
{
   debug_log "fstab_update" "$*"
   local FSTAB FSTABTMP

   FSTAB="$1/etc/fstab"
   FSTABTMP=$FSTAB$$
   mkdir -p $1/etc $1/mnt
   cat $FSTAB 2>/dev/null | grep -v "$FSTABLLFLAG" >$FSTABTMP

   echo "####  以下内容为Archlive启动时候自动添加 ####" >$FSTABTMP
   ROOT=$(cmdline_value root)
   #ROOT=$root
   if [ "x$ROOT" != "x" ] && [ "$ROOT" != "/dev/ram0" ]; then
	FS=$(device_filesystem $ROOT)
	OPT=$(fs_options $FS fstab)
	fstab_add_line $FSTABTMP $ROOT / $FS $OPT
   else
	fstab_add_line $FSTABTMP none / aufs defaults
   fi 

   cdrom_devices=$(list_cdrom_devices)
   debug_log "cdrom_devices=$(echo $cdrom_devices)"
#   list_cdrom_devices | while read DEVICE; do
   for DEVICE in ${cdrom_devices}; do
      MNT=$(device_mountdir $DEVICE)
      FS=$(device_filesystem $DEVICE)
      if [ "$FS" = "" ]; then FS=iso9660; fi
      mkdir -p "$1/$MNT"
      fstab_add_line $FSTABTMP "#$DEVICE" $MNT $FS $(fs_options $FS fstab)
   done

   partition_devices=$(list_partition_devices)
   debug_log "partition_devices=$(echo ${partition_devices})"
#   list_partition_devices | while read DEVICE; do
   for DEVICE in ${partition_devices}; do
      MNT=$(device_mountdir $DEVICE)
      FS=$(device_filesystem $DEVICE)
      OPT=$(fs_options $FS fstab)

      if [ "$FS" = "swap" ]; then
         fstab_add_line $FSTABTMP $DEVICE $MNT $FS $OPT
      fi

      # If the partition has a valid filesystem, add it to fstab
      if is_supported_filesystem "$FS"; then
	if [ "x$OPT" != "x" ]; then
	   fstab_add_line $FSTABTMP $DEVICE $MNT $FS $OPT
	   mkdir -p "$1/$MNT"
	fi
      fi
   done

   fstab_add_line $FSTABTMP none /proc proc defaults
   fstab_add_line $FSTABTMP none /sys sysfs defaults
   fstab_add_line $FSTABTMP none /dev/pts devpts gid=5,mode=620
   fstab_add_line $FSTABTMP tmpfs /dev/shm tmpfs defaults
   #fstab_add_line $FSTABTMP tmpfs /tmp tmpfs defaults
   fstab_add_line $FSTABTMP tmpfs /tmp tmpfs defaults,mode=1777
   #fstab_add_line $FSTABTMP tmpfs /var/tmp tmpfs defaults,mode=1777
   #fstab_add_line $FSTABTMP tmpfs /var/log tmpfs defaults,mode=0755,size=1M

   mv -f $FSTABTMP $FSTAB
}

# create correct fstab file in $1/etc/fstab only with aufs,proc,sysfs and devpts
# No partition will be mounted and mount point created
# HAL is going to manage mount points and medias
fstab_clean()
{
   debug_log "fstab_update" "$*"
   local FSTAB FSTABTMP

   FSTAB="$1/etc/fstab"
   FSTABTMP=$FSTAB$$
   mkdir -p $1/etc $1/mnt
   cat $FSTAB 2>/dev/null | grep -v "$FSTABLLFLAG" >$FSTABTMP

   fstab_add_line $FSTABTMP aufs / aufs defaults
   fstab_add_line $FSTABTMP proc /proc proc defaults
   fstab_add_line $FSTABTMP sysfs /sys sysfs defaults
   fstab_add_line $FSTABTMP devpts /dev/pts devpts gid=5,mode=620
   fstab_add_line $FSTABTMP tmpfs /dev/shm tmpfs defaults
   mv -f $FSTABTMP $FSTAB
}

# rcopy is a recursive cp, which copies also symlink's real source
# $1 = source (may be a regular file or symlink)
# $2 = target PARENT
#
rcopy()
{
   debug_log "rcopy $1 $2"
   CWD=$(pwd)
   if [ "$ROOT" = "" ]; then cd /; else cd $ROOT; fi
   #SOURCE="./${1:${#ROOT}}"
   SOURCE="${1:${#ROOT}}"
   DEST="$2"
   if [ -L "$SOURCE" ]; then
      REALPATH=$(readlink -f "$SOURCE")
      cp --parent -a "$REALPATH" "$DEST"
      ln -sf "$REALPATH" "$DEST/$SOURCE"
   else
      cp --parent -a "$SOURCE" "$DEST"
   fi
   if [ "$?" -ne 0 ]; then
      echo ""
      echo "---------------------------"
      echo "Error occured while trying to copy \"$SOURCE\""
      echo "Possible reason: not enough free space in initrd OR source doesn't exist."
      echo "If space is your issue, see '.config' file to increase size of initrd."
      echo "If source file doesn't exist, it's a big problem. See DOC/requirements.txt"
      echo "---------------------------"
      exit 1
   fi
   cd $CWD
}

# copy file/dir only if it exists, else skip with no error
# $1 = source (may not exist)
# $2 = target PARENT
#
rcopy_ex()
{
   debug_log "rcopy_ex $1 $2"
   if [ -a "$1" ]; then
      rcopy "$1" "$2"
   fi
}


# you must have all the locales available in your running system,
# so make sure to install glibc package and glibc-i18n

# $1 = source file or directory
# $2 = target file or directory
# $3 = root which is used (will be stripped)
cp_parents()
{
   local SOURCE TARGET ROOT STRIP

   SOURCE="$1"
   TARGET="$2"
   ROOT="$3"

   if [ -e "$SOURCE" -a "$#" = "3" ]; then
      STRIP=$(dirname ${SOURCE:${#ROOT}})
      mkdir -p $TARGET/$STRIP
      cp -a $SOURCE $TARGET/$STRIP
   fi
}

# $1 = from dir (eg /)
# $2 = to dir
# $3..$n = language(s)
#
copylocales()
{
   local ROOT TARGET ADDLOCALE MYLOCALE MYALIAS MYGCONV MYI18NLOC NAME ALIAS LOCA LOCAC CHAR GCONV GCONVDEP

   ROOT="$1"; shift
   TARGET="$1"; shift
   ADDLOCALE="$*"

   MYLOCALE=$ROOT/usr/lib/locale # system locales (sorting, days in week, etc)
   MYI18NLOC=$ROOT/usr/share/i18n/locales # the same like above
   MYALIAS=$ROOT/usr/share/locale/locale.alias # eg. to translate 'czech' to 'cs_CZ.ISO-8859-2'
   MYGCONV=$ROOT/usr/lib/gconv # codepages (compiled charmaps I guess)

   for NAME in $(echo $ADDLOCALE | tr ',' ' '); do
      ALIAS=$(cat $MYALIAS | egrep ^$NAME[[:space:]])
      if [ "$ALIAS" = "" ]; then continue; fi
      LOCA=$(echo $ALIAS | cut -d " " -f 2- | cut -d "." -f 1)
      CHAR=$(echo $ALIAS | cut -d "." -f 2 | sed -r 's/-/-?/g')
      # echo adding locales for $NAME:$LOCA:$CHAR | tr -d '?'

      cp_parents $MYALIAS $TARGET $ROOT
      cp_parents $MYI18NLOC/$LOCA $TARGET $ROOT
      cp_parents $MYGCONV/gconv-modules $TARGET $ROOT

      GCONV=$(find $MYGCONV | while read LINE; do echo $LINE@$(echo $LINE | tr -d "-"); done | egrep -i "/$CHAR.so\$" | head -n 1)
      GCONV=$(echo $GCONV | cut -d "@" -f 1)
      cp_parents $GCONV $TARGET $ROOT

      GCONVDEP=$(ldd $GCONV | grep -v linux-gate.so | grep -v libc.so | grep -v ld-linux.so)
      GCONVDEP=$(echo $GCONVDEP | cut -d " " -f 1)
      if [ "$GCONVDEP" ]; then cp_parents $MYGCONV/$GCONVDEP $TARGET $ROOT; fi

      LOCAC=$(find $MYLOCALE | egrep -i "/$LOCA(\\.$CHAR)?\$" | head -n 1)
      cp_parents $LOCAC $TARGET $ROOT
      cp_parents $ROOT/usrlocale $TARGET $ROOT
   done
}

mksquashfs_module () {
   plain -n "将${1} 用 $MKSQUASHFS 进行压缩..."
   [ -d ${1}/usr/share/fonts ] && chmod 755 ${1}/usr/share/fonts
   [ -d ${1}/etc/fonts ] && chmod 755 ${1}/etc/fonts
   OPTION="-noappend ${OPTION}"
   if [ "${QUIET}" = "y" ]; then
	   $MKSQUASHFS ${1} ${2} ${OPTION} >/dev/null
   else
	$MKSQUASHFS ${1} ${2} ${OPTION}
   fi
   chmod 0755 ${2}
   green "完成!"
}
